/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

// ============================================================================================================================
#include <dylab/simobject/ComModelStructure.h>
#include <dylab/simobject/ComModelStructureLoader.h>

// ============================================================================================================================
// dylab::ComModelStructure::IMassPoints::MassPoint::HistoryStates
// ============================================================================================================================
dylab::ComModelStructure::IMassPoints::MassPoint::HistoryStates::HistoryStates(uint32_t _size, const State & _currState)
: mSize(_size)
{ 
	DYLAB_ASSERT(mSize > 0);
	DYLAB_ASSERT(mSize <= MAX_HISTORY_SIZE);
	for (uint32_t i = 0; i < mSize; i++)
	{
		State * s = new State(_currState);
		DYLAB_ASSERT(s != NULL);
		mStates[i] = s;
	}
}
//-----------------------------------------------------------------------------
dylab::ComModelStructure::IMassPoints::MassPoint::HistoryStates::~HistoryStates()
{
	for (uint32_t i = 0; i < mSize; i++)
		delete mStates[i];
}
//-----------------------------------------------------------------------------
void dylab::ComModelStructure::IMassPoints::MassPoint::HistoryStates::storeState(const State & _currState)
{
	for (int32_t i = mSize; i > 0; i--)
	{
		if (i-2 >= 0)
			*(mStates[i-1]) = *(mStates[i-2]);
		else
			*(mStates[i-1]) = _currState;
	}
}
//-----------------------------------------------------------------------------

// ============================================================================================================================
// dylab::ComModelStructure::IMassPoints::MassPoint
// ============================================================================================================================
void dylab::ComModelStructure::IMassPoints::MassPoint::reserveHistoryStates(uint32_t _noStates)
{
	if (mHistoryStates != NULL) 
	{
		if (mHistoryStates->getSize() < _noStates)
			delete mHistoryStates;
		else
			return;
	}
	mHistoryStates = new HistoryStates(_noStates, mCurrState);
	DYLAB_ASSERT(mHistoryStates != NULL);
}
//-----------------------------------------------------------------------------

// ============================================================================================================================
// dylab::ComModelStructure::IMassPoints
// ============================================================================================================================
void dylab::ComModelStructure::IMassPoints::reserveHistoryStates(uint32_t _noStates)
{
    // for each structure massPoint reserve the requested number of its history states
    for (ComModelStructure::IMassPoints::MassPointMap::Iterator mp(massPointIterator()); mp.isValid(); mp++)
    {   
		mp->reserveHistoryStates(_noStates);
	}
}
//-----------------------------------------------------------------------------
void dylab::ComModelStructure::IMassPoints::applyPointMagnetForce(const Vector3 & _magnet, real_t _radius, 
																  real_t _forceCoeff, real_t _attenuationExp)
{
    // for all mass points
    for (ComModelStructure::IMassPoints::MassPointMap::Iterator mp(massPointIterator()); mp.isValid(); mp++)
    {
        // compute the force direction
        Vector3 forceDir = _magnet - mp->currState().position;

		// compute the length to the mass point
        real_t dist = forceDir.length();        

		// if the length is not near zero and is lower than the specified radius
		if (!Math::almostZero(dist) && (dist <= _radius)) 
        {         
			// compute force magnitude
            real_t forceMag = _forceCoeff * (1 - pow(dist, _attenuationExp) / pow(_radius, _attenuationExp));

			// normalise the force vector
            forceDir.normalise();

			// add the normalized force direction multiplied by the force magnitude
			mp->currState().force += forceDir * forceMag;
        }
    }
}
//-----------------------------------------------------------------------------
void dylab::ComModelStructure::IMassPoints::applyPointMagnetForce(MassPoint::id_t _massPointId, real_t _radius, 
																  real_t _forceCoeff, real_t _attenuationExp)
{    
	// retrieve the mass point's position and delegate the call
    applyPointMagnetForce(massPointReader().get(_massPointId)->currState().position, _radius, _forceCoeff, _attenuationExp);
}
//-----------------------------------------------------------------------------

// ============================================================================================================================
// dylab::ComModelStructure::ITetrahedralVolume
// ============================================================================================================================
void dylab::ComModelStructure::ITetrahedralVolume::generateCubeSection(
	IMassPoints::MassPoint::id_t _mp1, IMassPoints::MassPoint::id_t _mp2, IMassPoints::MassPoint::id_t _mp3, 
	IMassPoints::MassPoint::id_t _mp4, IMassPoints::MassPoint::id_t _mp5, IMassPoints::MassPoint::id_t _mp6, 
	IMassPoints::MassPoint::id_t _mp7, IMassPoints::MassPoint::id_t _mp8)
{
    // add all the necessary tetrahedra
    createTetrahedron(_mp1, _mp2, _mp6, _mp3);
    createTetrahedron(_mp1, _mp5, _mp8, _mp6);
    createTetrahedron(_mp4, _mp8, _mp3, _mp1);
    createTetrahedron(_mp3, _mp7, _mp6, _mp8);
    createTetrahedron(_mp1, _mp3, _mp6, _mp8);                        
}
//-----------------------------------------------------------------------------

// ============================================================================================================================
// dylab::ComModelStructure
// ============================================================================================================================
dylab::ComModelStructure::ComModelStructure(const String & _componentName)
: ComponentBase<ComModelStructure>(_componentName)
{
}
//-----------------------------------------------------------------------------
void dylab::ComModelStructure::loadFromPropertyTree(const PropertyTree & _pt)
{
	// retrieve the loader property tree
	const PropertyTree & loaderPT = _pt.getNode("loader")->getChildren();

	// create loader component
	ComModelStructureLoader * loader = ComModelStructureLoader::create(IComponent::loadComName(loaderPT));

	// assign the loader this component model
	loader->setModelToLoad(this);

	// load the loader, which in turn load this model
	loader->loadFromPropertyTree(loaderPT);

	// delete the loader component
	delete loader;
}
//-----------------------------------------------------------------------------